package com.tanhua.app.service;

import cn.hutool.core.util.PageUtil;
import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.tanhua.api.UserInfoApi;
import com.tanhua.api.VideoApi;
import com.tanhua.app.exception.BusinessException;
import com.tanhua.app.exception.ErrorResult;
import com.tanhua.app.interceptor.UserHolder;
import com.tanhua.commons.utils.Constants;
import com.tanhua.config.template.OssTemplate;
import com.tanhua.model.mongo.Video;
import com.tanhua.model.pojo.UserInfo;
import com.tanhua.model.vo.PageResult;
import com.tanhua.model.vo.VideoVo;
import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class SmallVideosService {

    @Autowired
    private MqMessageService mqMessageService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private FastFileStorageClient client;

    @Autowired
    private FdfsWebServer webServer;

    @Autowired
    private OssTemplate ossTemplate;

    @DubboReference
    private VideoApi videoApi;

    @DubboReference
    private UserInfoApi userInfoApi;

    //发布视频
    public void saveVideos(MultipartFile videoThumbnail, MultipartFile videoFile) throws IOException {
        //1 判断图片和视频是否为空，为空抛异常
        if (videoThumbnail == null || videoFile == null) {
            throw new BusinessException(ErrorResult.error());
        }

        //2 上传视频文件到FastDFS中
        String oriName = videoFile.getOriginalFilename();
        String extName = oriName.substring(oriName.lastIndexOf(".") + 1);
        StorePath storePath = client.uploadFile(videoFile.getInputStream(), videoFile.getSize(), extName, null);
        String videoUrl = webServer.getWebServerUrl() + storePath.getFullPath();

        //3 上传视频封面图片到阿里OSS中
        String picUrl = ossTemplate.uploadOss(videoThumbnail.getInputStream(), videoThumbnail.getOriginalFilename());

        //4 构建小视频数据
        Video video = new Video();
        video.setCreated(System.currentTimeMillis());
        video.setUserId(UserHolder.getUserId());
        video.setText("小视频");
        video.setPicUrl(picUrl);
        video.setVideoUrl(videoUrl);

        //5 调用API保存小视频数据
        String videoId = videoApi.save(video);

        //发送消息
        mqMessageService.sendLogMessage(UserHolder.getUserId(), "0301", "video", videoId);
    }

    //videos:缓存数据的命名空间
    @Cacheable(value = "videos",
            //key每个用户视频查询结果不一样  userId_page_pagesize   106_1_10   106_2_10  106_1_5
            //使用SpringEL表达式
            key = "T(com.tanhua.app.interceptor.UserHolder).getUserId()+'_'+#page+'_'+#pagesize")
    //分页查询小视频列表  先查询推荐数据 VIDEOS_RECOMMEND_106 100002,100003,100004,100006,100007,100008
    public PageResult<VideoVo> queryVideoList(Integer page, Integer pagesize) {
        //1 查询redis中的推荐视频 VIDEOS_RECOMMEND_106 100002,100003,100004,100006,100007,100008
        String key = Constants.VIDEOS_RECOMMEND + UserHolder.getUserId();
        String value = redisTemplate.opsForValue().get(key);

        //推荐数据有8条  每页显示5条  1页:0,1,2,3,4  2页:5,6,7  3页没有数据
        //1页： redis的推荐数据   0 1 2 3 4   Redis第1页
        //2页： redis的推荐数据   5 6 7       Redis第2页
        //3页： 直接查询MongoDB              MongoDB第1页
        //4页： 直接查询MongoDB              MongoDB第2页
        //........

        List<Video> videoList = new ArrayList<>();//存放小视频数据的集合
        //redis中的总页数
        int redisPages = 0;

        //2 判断是否有推荐数据
        if (StringUtils.isNotBlank(value)) {
            //2.1 如果有推荐数据，查询推荐数据
            //获取推荐数据的vid数组 100002,100003,100004,100006,100007,100008
            String[] vidArr = value.split(",");
            //判断当前页是否有足够的数据     8条数据  每页实现5条   1页:0,1,2,3,4  2页:5,6,7  3页没有数据
            if (vidArr.length > (page - 1) * pagesize) {
                //对数组进行分页
                List<Long> vids = Arrays.stream(vidArr)
                        .skip((page - 1) * pagesize)
                        .limit(pagesize)
                        .map(vid -> Long.parseLong(vid))
                        .collect(Collectors.toList());

                //根据分页的结果  vids 查询推荐视频
                videoList = videoApi.findByVids(vids);
            }

            //计算Redis的总页数
            redisPages = PageUtil.totalPage(vidArr.length, pagesize);
        }

        //推荐数据有8条  每页显示5条  1页:0,1,2,3,4  2页:5,6,7  3页没有数据
        //1页： redis的推荐数据   0 1 2 3 4   Redis第1页
        //2页： redis的推荐数据   5 6 7       Redis第2页
        //3页： 直接查询MongoDB              MongoDB第1页
        //4页： 直接查询MongoDB              MongoDB第2页
        //........
        //根据redis的总页数，查询MongoDB    例如redis一共有两页    1redis 2redis 3MongoDB 4MongoDB
        if (page > redisPages) {
            //2.2 如果没有推荐数据，直接分页查询MongoDB，获取分页数据，按照发布时间倒序排序
            videoList = videoApi.findByPage(page - redisPages, pagesize);//MongoDB分页查询，页数要减掉redis的总页数
        }

        //3 小视频结果，获取发布小视频的作者的用户详情
        List<Long> ids = videoList.stream()
                .map(Video::getUserId).collect(Collectors.toList());
        Map<Long, UserInfo> map = userInfoApi.findByIds(ids).stream()
                .collect(Collectors.toMap(UserInfo::getId, Function.identity()));

        //4 封装vos数据
        List<VideoVo> vos = new ArrayList<>();
        for (Video video : videoList) {
            UserInfo userInfo = map.get(video.getUserId());
            if (userInfo != null) {
                VideoVo vo = VideoVo.init(userInfo, video);
                vos.add(vo);
            }
        }

        //5 封装分页对象，返回数据
        return new PageResult<>(page, pagesize, 0, vos);
    }
}
